﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using DMS.Integration.CopyServ;
using DMS.Integration.ListsServ;
using DMS.Integration.WebServ;

namespace DMS.Integration
{
    /// <summary>
    /// This is a helper class for Invoices and Receipts Type document and metadata update and retrieve between sharepoint and third party application
    /// Methods required:
    /// 1. UploadFile - upload PDF document to sharepoint and get ID
    /// 1. UploadFile - upload PDF document to sharepoint and get ID and out Url
    /// 2. UpdateMetadata - save metadata entry to sharepoint
    /// 3. GetDocumentById - get the pdf document url by passing the invoiceId/receiptId
    /// 
    /// </summary>
    public class SharePointHelper
    {
        private static string _receiptWeb = "Receipts";
        private static string _invoiceWeb = "Invoices";

        private static string _receiptListDocumentName = "ReceiptDocuments";
        private static string _invoiceListDocumentName = "InvoiceDocuments";

        private static string _receiptMetadataListName = "Receipts";
        private static string _invoiceMetadataListName = "Invoices";

        private static NetworkCredential _credential;
        private static string _siteCollectionUrl;

        private static string _branchListName = "Branches";

        public SharePointHelper()
        {

            _siteCollectionUrl = ConfigurationManager.AppSettings["SharePointDMSSiteUrl"];

            if( _siteCollectionUrl.Length == 0)
            {
                throw new Exception("Web Application Url and Site Collection Url can't be empty.");
            }


            if(!_siteCollectionUrl.EndsWith("/"))
            {
                _siteCollectionUrl = _siteCollectionUrl + "/";
            }

            string username = ConfigurationManager.AppSettings["SPWebServiceUsername"];
            string password = ConfigurationManager.AppSettings["SPWebServicePassword"];
            string domain = ConfigurationManager.AppSettings["SPWebServiceDomain"];
            _credential = new NetworkCredential(username,password,domain);
        }


        internal enum Operation
        {
            UploadFile,
            AddMetadata
        }

        /// <summary>
        /// upload the document from NAV and return the document ID 
        /// </summary>
        /// <param name="docType"></param>
        /// <param name="year"></param>
        /// <param name="filename">the PDF document file name, should always be the format {auctionid}-{itemid}.pdf</param>
        /// <param name="contents"></param>
        /// <returns></returns>
        public int UploadFile(DocumentType docType, int year, string filename, byte[] contents)
        {

            bool success = false;
            string siteUrl = _siteCollectionUrl;
            string copyServiceUrl = string.Format("{0}_vti_bin/copy.asmx", _siteCollectionUrl);
            var copyService = new Copy { Url = copyServiceUrl };
            
            //the account must have full control permission to the destination list/document library
            copyService.Credentials = _credential;

            string fileUrl = string.Empty;
            
            //get the folder name to store the document
            string folderName = GetFolderName(filename);


            //check if the document folder exist, create if not

            string listName = string.Empty;

            if(docType == DocumentType.Receipt)
            {

                fileUrl = string.Format("{0}{1}/{2}/{3}/{4}/{5}", 
                                        _siteCollectionUrl,
                                        _receiptWeb,
                                        year,
                                        _receiptListDocumentName,
                                        folderName,
                                        filename);
                siteUrl = String.Format("{0}{1}/{2}/", _siteCollectionUrl, _receiptWeb, year);
                CreateFolderIfNotExist(siteUrl, _receiptListDocumentName, folderName);
                listName = _receiptListDocumentName;
            }else //if(docType == DocumentType.Invoice)
            {
                fileUrl = string.Format("{0}{1}/{2}/{3}/{4}/{5}",
                        _siteCollectionUrl,
                        _invoiceWeb,
                        year,
                        _invoiceListDocumentName,
                        folderName,
                        filename);

                siteUrl = String.Format("{0}{1}/{2}/", _siteCollectionUrl, _invoiceWeb, year);
                CreateFolderIfNotExist(siteUrl, _invoiceListDocumentName, folderName);
                listName = _invoiceListDocumentName;
            }

            string[] destinationUrls = {fileUrl};

            FieldInformation fFieldInfo = new FieldInformation()
                                              {
                                                  DisplayName = "Description",
                                                  Type = FieldType.Text,
                                                  Value = fileUrl
                                              };
            FieldInformation[] fieldInformations = { fFieldInfo };

            CopyResult[] results;
            uint copyResult = copyService.CopyIntoItems(fileUrl, destinationUrls, fieldInformations, contents,
                                                        out results);

            string fileName = Path.GetFileName(fileUrl);
            string queryXml = "<Where><And><Eq><FieldRef Name='FileLeafRef' /><Value Type='File'>" + fileName +
             "</Value></Eq><Eq><FieldRef Name='ContentType' /><Value Type='Text'>Document</Value></Eq></And></Where>";

            string itemId = GetItemID(siteUrl, listName, queryXml, folderName);

            int result = Convert.ToInt32(itemId);

            return result;
        }


        public bool UpdateMetadata(DocumentType type, IMetadata metadata)
        {

            int year = metadata.Year;
            bool success = false;
            string siteUrl = string.Empty;
            string listName = string.Empty;
            string itemRootFolder = string.Empty;
            string folderName = GetFolderName(metadata.Id);
            if(type == DocumentType.Receipt)
            {
                siteUrl = string.Format("{0}{1}/{2}/", _siteCollectionUrl, _receiptWeb, year);
                listName = _receiptMetadataListName;

                CreateFolderIfNotExist(siteUrl, _receiptMetadataListName, folderName);
                itemRootFolder = string.Format("{0}Lists/{1}/{2}", siteUrl, _receiptMetadataListName, folderName);
            }else if(type == DocumentType.Invoice)
            {
                siteUrl = string.Format("{0}{1}/{2}/", _siteCollectionUrl, _invoiceWeb, year);
                listName = _invoiceMetadataListName;

                CreateFolderIfNotExist(siteUrl, _invoiceMetadataListName, folderName);
                
                itemRootFolder = string.Format("{0}Lists/{1}/{2}", siteUrl, _invoiceMetadataListName, folderName);
            }

            var listService = new Lists();

            listService.Url = siteUrl + "_vti_bin/lists.asmx";

            listService.Credentials = _credential;


            XmlNode ndListView = listService.GetListAndView(listName, "");
            string strListId = ndListView.ChildNodes[0].Attributes["Name"].Value;
            string strViewId = ndListView.ChildNodes[1].Attributes["Name"].Value;

            var doc = new XmlDocument();
            XmlElement batch = doc.CreateElement("Batch");


            batch.SetAttribute("OnError", "Continue");
            batch.SetAttribute("ListVersion", "1");
            batch.SetAttribute("ViewName", strViewId);
            batch.SetAttribute("RootFolder", itemRootFolder);

            batch.InnerXml = GetMetaDataCaml(metadata.ToStringDictionary());


            XmlNode node = listService.UpdateListItems(strListId, batch);
            if (node != null)
            {
                if (node.InnerText.Equals("0x00000000"))
                    return true;
            }
            return false;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id">invoice id/receipt id</param>
        /// <param name="documentType">type of document</param>
        /// <returns>the full url of the pdf document</returns>
        public string GetDocumentUrlById(string id, DocumentType documentType)
        {
            var websService = new Webs();
            websService.Url = string.Format("{0}_vti_bin/webs.asmx",_siteCollectionUrl);
            //websService.Url = documentType == DocumentType.Invoice
            //                      ? _siteCollectionUrl + "Invoices/"
            //                      : _siteCollectionUrl + "Receipts";
            websService.Credentials = _credential;


            XmlNode node = websService.GetAllSubWebCollection();
            XmlNodeList nodes = node.SelectNodes("*");

            List<string> subWebUrls = new List<string>();

            foreach (XmlNode xmlNode in nodes)
            {
                string url = xmlNode.Attributes["Url"].InnerText;
                if(Regex.IsMatch(url, documentType == DocumentType.Invoice ? _siteCollectionUrl + @"Invoices/\d+$" : _siteCollectionUrl + @"Receipts/\d+$"))
                {
                    subWebUrls.Add(url);
                }
            }
            subWebUrls.Sort();
            subWebUrls.Reverse();

            foreach (string subWebUrl in subWebUrls)
            {
                string documentUrl = SearchDocumentByTitle(subWebUrl, id, documentType);
                if (!string.IsNullOrEmpty(documentUrl))
                    return documentUrl;
            }

            return string.Empty;
        }

        private string SearchDocumentByTitle(string subWebUrl, string id, DocumentType documentType)
        {
            if (!subWebUrl.EndsWith("/"))
                subWebUrl = subWebUrl + "/";
            var listReference = new Lists();
            string strListName = documentType == DocumentType.Invoice
                                     ? _invoiceListDocumentName
                                     : _receiptListDocumentName;

            string folder = GetFolderName(id);
            try
            {
                listReference.Credentials = _credential;
                listReference.Url = subWebUrl + "_vti_bin/lists.asmx";
                var xmlDoc = new XmlDocument();
                var query = xmlDoc.CreateElement("Query");
                var viewFields = xmlDoc.CreateElement("ViewFields");
                XmlElement queryOptions = xmlDoc.CreateElement("QueryOptions");
                string queryXml = "<Where><And><Eq><FieldRef Name='FileLeafRef' /><Value Type='File'>{0}.pdf</Value></Eq><Eq><FieldRef Name='ContentType' /><Value Type='Text'>Document</Value></Eq></And></Where>";
                query.InnerXml = string.Format(queryXml, id);
                viewFields.InnerXml = "<FieldRef Name=\"Title\" /><FieldRef Name=\"FileLeafRef\" />";
                queryOptions.InnerXml = "<Folder>" + folder + "</Folder>";
                XmlNode items;
                if (String.IsNullOrEmpty(folder))
                    items = listReference.GetListItems(strListName, string.Empty, query, viewFields, string.Empty, null, null);
                else
                    items = listReference.GetListItems(strListName, string.Empty, query, viewFields, string.Empty,
                                                                   queryOptions, null);
                bool findRecord = false;
                foreach (XmlNode node in items)
                {
                    if (node.Name == "rs:data")
                    {
                        int count = Convert.ToInt32(node.Attributes["ItemCount"].InnerText);
                        if(count == 1)
                        {
                            findRecord = true;
                            break;
                        }
                    }
                }

                if (findRecord)
                    return string.Format("{0}{1}/{2}/{3}.pdf", subWebUrl, strListName, folder, id);
            }
            catch (Exception ex)
            {
                throw new Exception("Error on retrieving Item ID: " + ex.Message);
            }
            finally
            {
                if (listReference != null)
                    listReference.Dispose();
            }
            return string.Empty;
        }

        #region helper methods

        //private string GetFolderName(string filename)
        //{
        //    string folderName = filename.Substring(filename.LastIndexOf("-", StringComparison.CurrentCultureIgnoreCase) + 1, filename.IndexOf(".", System.StringComparison.Ordinal) - filename.LastIndexOf("-", StringComparison.CurrentCultureIgnoreCase) - 1);
        //    return folderName;
        //}

        private string GetFolderName(string filename)
        {
            int suffixdot = filename.IndexOf(".", System.StringComparison.Ordinal);

            if(suffixdot != -1)
            {
                filename = filename.Substring(0, suffixdot);
            }

            string md5 = CalculateMD5Hash(filename);
            return md5.Substring(0, 2);
        }
        public string CalculateMD5Hash(string input)
        {
            // step 1, calculate MD5 hash from input
            MD5 md5 = System.Security.Cryptography.MD5.Create();
            byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
            byte[] hash = md5.ComputeHash(inputBytes);

            // step 2, convert byte array to hex string
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString();
        }
        public string GetBranchLookupDataByName(string branchName)
        {
            var listReference = new Lists();
            try
            {
                listReference.Credentials = _credential;
                listReference.Url = _siteCollectionUrl + "_vti_bin/lists.asmx";


                var xmlDoc = new XmlDocument();
                var query = xmlDoc.CreateElement("Query");
                var viewFields = xmlDoc.CreateElement("ViewFields");
                string queryXml = "<Where><Eq><FieldRef Name='Title' /><Value Type='Text'>" + branchName + "</Value></Eq></Where>";
                query.InnerXml = queryXml;
                viewFields.InnerXml = "<FieldRef Name=\"ID\" />";
                XmlNode items;

                items = listReference.GetListItems(_branchListName, string.Empty, query, viewFields, string.Empty, null, null);

                int id = 0;

                foreach (XmlNode node in items)
                {
                    if (node.Name == "rs:data")
                    {
                        for (int i = 0; i < node.ChildNodes.Count; i++)
                        {
                            if (node.ChildNodes[i].Name == "z:row")
                            {
                                var xmlAttributeCollection = node.ChildNodes[i].Attributes;
                                if (xmlAttributeCollection != null) id = Convert.ToInt32(xmlAttributeCollection["ows_ID"].Value);
                                break;
                            }
                        }
                    }
                }

                if(id != 0)
                {
                    return string.Format("{0};#{1}", id, branchName);
                }
            }
            catch (Exception ex)
            {
                
                throw new Exception(ex.Message);
            }
            return string.Empty;
        }

		public string GetBranchLookupDataByCode(int code)
		{
		    string branchCode = code.ToString();
            var listReference = new Lists();
            try
            {
                listReference.Credentials = _credential;
                listReference.Url = _siteCollectionUrl + "_vti_bin/lists.asmx";


                var xmlDoc = new XmlDocument();
                var query = xmlDoc.CreateElement("Query");
                var viewFields = xmlDoc.CreateElement("ViewFields");
                string queryXml = "<Where><Eq><FieldRef Name='BranchCode' /><Value Type='Text'>" + branchCode + "</Value></Eq></Where>";
                query.InnerXml = queryXml;
                viewFields.InnerXml = "<FieldRef Name=\"ID\" />";
                XmlNode items;

                items = listReference.GetListItems(_branchListName, string.Empty, query, viewFields, string.Empty, null, null);

                int id = 0;
                string branchName = string.Empty;
                foreach (XmlNode node in items)
                {
                    if (node.Name == "rs:data")
                    {
                        for (int i = 0; i < node.ChildNodes.Count; i++)
                        {
                            if (node.ChildNodes[i].Name == "z:row")
                            {
                                var xmlAttributeCollection = node.ChildNodes[i].Attributes;
                                if (xmlAttributeCollection != null)
                                {
                                    id = Convert.ToInt32(xmlAttributeCollection["ows_ID"].Value);
                                    branchName = xmlAttributeCollection["ows_Title"].Value;
                                    break;
                                }
                            }
                        }
                    }
                }

                if(id != 0)
                {
                    return string.Format("{0};#{1}", id, branchName);
                }
            }
            catch (Exception ex)
            {
                
                throw new Exception(ex.Message);
            }
            return string.Empty;
        }

        /// <summary>
        /// This function return item id based on query
        /// </summary>
        /// <param name="siteUrl"> </param>
        /// <param name="strListName">list name</param>
        /// <param name="queryXml"> </param>
        /// <param name="destinationFolderPath"> </param>
        /// <returns>look id</returns>
        private string GetItemID(string siteUrl, string strListName, string queryXml, string destinationFolderPath)
        {
            if (!siteUrl.EndsWith("/"))
                siteUrl = siteUrl + "/";
	        var listReference = new Lists();
	        string lookupId = string.Empty;
	        try
	        {
		        listReference.Credentials = _credential;
                listReference.Url = siteUrl + "_vti_bin/lists.asmx";
		        var xmlDoc = new XmlDocument();
		        var query = xmlDoc.CreateElement("Query");
		        var viewFields = xmlDoc.CreateElement("ViewFields");
		        XmlElement queryOptions = xmlDoc.CreateElement("QueryOptions");
		        query.InnerXml = queryXml;
		        viewFields.InnerXml = "<FieldRef Name=\"ID\" />";
		        queryOptions.InnerXml = "<Folder>" + destinationFolderPath + "</Folder>";
		        XmlNode items;
		        if (String.IsNullOrEmpty(destinationFolderPath))
			        items = listReference.GetListItems(strListName, string.Empty, query, viewFields, string.Empty, null, null);
		        else
			        items = listReference.GetListItems(strListName, string.Empty, query, viewFields, string.Empty, 
                                                                   queryOptions, null);
 
		        foreach (XmlNode node in items)
		        {
			        if (node.Name == "rs:data")
			        {
				        for (int i = 0; i < node.ChildNodes.Count; i++)
				        {
					        if (node.ChildNodes[i].Name == "z:row")
					        {
					            var xmlAttributeCollection = node.ChildNodes[i].Attributes;
					            if (xmlAttributeCollection != null) lookupId = xmlAttributeCollection["ows_ID"].Value;
					            break;
					        }
				        }
			        }
		        }
	        }
	        catch (Exception ex)
	        {
                throw new Exception("Error on retrieving Item ID: " + ex.Message);
	        }
	        finally
	        {
		        if (listReference != null)
			        listReference.Dispose();
	        }
 
	        return lookupId;
        }

        /// <summary>
        /// Read file and return bytes
        /// </summary>
        /// <param name="path">file path</param>
        /// <returns>file bytes</returns>
        public static byte[] GetFileFromFileSystem(string path)
        {
            byte[] fileBytes = null;

            if (File.Exists(path))
            {
                //read the file.
                using (FileStream fs = File.Open(path, FileMode.Open))
                {
                    fileBytes = new byte[fs.Length];
                    fs.Position = 0;
                    fs.Read(fileBytes, 0, Convert.ToInt32(fs.Length));
                }
            }
            return fileBytes;
        }

        private void CreateFolderIfNotExist(string siteUrl,  string docLibraryName, string folderName)
        {
            var listService = new Lists();
            if(!siteUrl.EndsWith("/"))
                siteUrl = siteUrl + "/";

            listService.Url = siteUrl +"_vti_bin/lists.asmx";
            listService.Credentials = _credential;
            //XmlNode list = listService.GetList(docLibraryName);

            string query = "<mylistitemrequest><Query><Where><And><Eq><FieldRef Name=\"FSObjType\" /><Value Type=\"Lookup\">1</Value></Eq><Eq><FieldRef Name=\"FileLeafRef\" /><Value Type=\"Text\">{0}</Value></Eq></And></Where></Query><ViewFields><FieldRef Name=\"FileRef\" /><FieldRef Name=\"Title\" /></ViewFields><QueryOptions><ViewAttributes Scope=\"RecursiveAll\" /></QueryOptions></mylistitemrequest>";
            //string query =
            //    "<mylistitemrequest><Query><Where><And><Eq><FieldRef Name='FileLeafRef' /><Value Type='Text'>{0}</Value><FieldRef Name='FSObjType'/><Value Type='Text'>1</Value></Eq></And></Where></Query><QueryOptions><ViewAttributes Scope='RecursiveAll' /></QueryOptions></mylistitemrequest>";
            
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(String.Format(query,folderName));

            XmlNode queryNode = doc.SelectSingleNode("//Query");
            XmlNode viewNode = doc.SelectSingleNode("//ViewFields");
            XmlNode optionNode = doc.SelectSingleNode("//QueryOptions");

            XmlNode nodes = listService.GetListItems(docLibraryName, string.Empty, queryNode, viewNode, string.Empty,
                                                    optionNode, null);

            foreach (XmlNode node in nodes)
            {
                if(node.Name == "rs:data")
                {
                    if (node.Attributes != null)
                    {
                        int count = Convert.ToInt32(node.Attributes["ItemCount"].Value);
                        if(count == 0)
                        {
                            //create folder
                            string xmlconst = "<Batch OnError='Continue' ListVersion='1'><Method ID='1' Cmd='New'><Field Name='ID'>New</Field><Field Name='FSObjType'>1</Field><Field Name='BaseName'>{1}</Field></Method></Batch>";

                            doc.LoadXml(string.Format(xmlconst,siteUrl+docLibraryName,folderName));
                            
                            XmlNode batchNode = doc.SelectSingleNode("//Batch");
                            XmlNode resultNodes = listService.UpdateListItems(docLibraryName, batchNode);
                        }
                    }
                }
            }
        }

        private string GetMetaDataCaml(Hashtable fieldValuePairs)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<Method ID='1' Cmd='New'>");
            foreach (string key in fieldValuePairs.Keys)
            {
                sb.AppendFormat("<Field Name='{0}'>{1}</Field>", key, fieldValuePairs[key]);
            }
            sb.Append("</Method>");
            return sb.ToString();
        }
        #endregion


        /// <summary>
        /// Update metadata of document in document library
        /// You can generate query in below way:-
        ///string query = @"<Method ID=’1' Cmd=’Update’><Field Name=’ID’>" + itemID + "</Field>” +
        ///"<Field Name=’SPID’>" + spLookUpId + "</Field> </Method>";
        /// </summary>
        /// <param name="strListName">list name</param>
        /// <param name="strCAML">metadata information in query</param>
        //public void UpdateMetaData(string webUrl, string strListName, string strCAML)
        //{
        //    Lists listReference = new Lists();
        //    try
        //    {
        //        listReference.Credentials = _credential;

        //        string serviceUrl = string.Format("{0}_vti_bin/lists.asmx", webUrl);
        //        listReference.Url = serviceUrl;

        //        /*Get Name attribute values (GUIDs) for list and view. */
        //        System.Xml.XmlNode ndListView = listReference.GetListAndView(strListName, "");
        //        string strListID = ndListView.ChildNodes[0].Attributes["Name"].Value;
        //        string strViewID = ndListView.ChildNodes[1].Attributes["Name"].Value;

        //        /*Create an XmlDocument object and construct a Batch element and its
        //         attributes. Note that an empty ViewName parameter causes the method to use the default view. */
        //        System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
        //        System.Xml.XmlElement batchElement = doc.CreateElement("Batch");
        //        batchElement.SetAttribute("OnError", "Continue");
        //        batchElement.SetAttribute("ListVersion", "1");
        //        batchElement.SetAttribute("ViewName", strViewID);

        //        /*Specify methods for the batch post using CAML. To update or delete, 
        //         specify the ID of the item, and to update or add, specify 
        //         the value to place in the specified column.*/
        //        batchElement.InnerXml = strCAML;

        //        /*Update list items. This example uses the list GUID, which is recommended, 
        //         but the list display name will also work.*/
        //        listReference.UpdateListItems(strListID, batchElement);
        //    }
        //    catch (Exception ex)
        //    {
        //        //exception
        //    }
        //    finally
        //    {
        //        if (listReference != null)
        //            listReference.Dispose();
        //    }
        //}

    }


}
